Option Explicit

' ======================================================================
' Descriptor
' 列元素容器
' ======================================================================

' ======================================================================
' Implement
' ======================================================================

Implements ISqlFormatterElem

' ======================================================================
' Field
' ======================================================================

' ==================================================
' Field
' ==================================================
' case token
Private caseToken As SqlToken

' 组成当前列对象的所有元素
' IObjArrayRef<ISqlFormatterElem>
Private componentArray As IObjArrayRef

' end 元素的索引
' 默认值 = -1
Private endIndex As Long

' ==================================================
' ISqlFormatterElem Field
' ==================================================
' 前缀空格的数量
' 默认值 = 0
Private prefixSpaceCount As Integer

' 左侧括号的数量
Private leftParenthesisCount As Integer
' 右侧括号的数量
Private rightParenthesisCount As Integer

' ======================================================================
' Constructor
' ======================================================================
Private Sub Class_Initialize()
    ' 组成条件的所有元素
    Set componentArray = UtilsArrayRef.createRealObjArrayRef(4)

    ' end 元素的索引
    endIndex = -1

End Sub

Private Sub Class_Terminate()
    Set componentArray = Nothing
End Sub

' ======================================================================
' Public Function
' ======================================================================
' 设置 case token
Public Sub Init(ByRef t As SqlToken)
    Set caseToken = t
End Sub

' ======================================================================
' implements ISqlFormatterElem Function
' ======================================================================
' ==================================================
' ISqlFormatterElem -- 添加组成元素
' ==================================================
' 添加组成元素
Public Sub ISqlFormatterElem_addComponent(ByRef c As ISqlFormatterElem)
    ' ==================================================
    ' 1. 参数检查
    ' ==================================================
    ' 1. 检查: 如果元素是 `null` 则退出
    If c Is Nothing Then
        ' return
        Exit Sub
    End If

    ' ==================================================
    ' 2. 保存元素
    ' ==================================================
    ' 1. 将元素保存到元素数组中
    componentArray.Add c

    ' ==================================================
    ' 3. 处理 end 元素
    ' ==================================================
    ' 检查: 如果当前元素是 end，则保存该元素的索引
    If c.getType = SqlTokenType.KEY_END Then
        endIndex = componentArray.MaxIndex
    End If
End Sub

' 将元素添加到开头位置
Public Sub ISqlFormatterElem_addComponentToFirst(ByRef c As ISqlFormatterElem)
    ' noop
End Sub

' 能否将元素添加到开头位置
Public Function ISqlFormatterElem_canAddComponentToFirst() As Boolean
    ' return false
End Function

' ==================================================
' ISqlFormatterElem -- 对齐处理
' ==================================================
' 该方法是用来配置对齐的方法
' 返回元素内，最多一级深度下的对齐元素组
' 如果是列对象，则返回作用域各个组成元素内的条件组（主要是子查询）
' 如果是条件对象，则返回条件自身
' 如果是作用域对象，则返回作用域内各部分中的条件组合
' 如果是赋值语句，则返回语句自身
' 如果没有条件或者赋值语句，则返回 null
'
' @return IObjArrayRef<IObjArrayRef<ISqlFormatterAlignmentElem>>
Public Function ISqlFormatterElem_getAlignmentElemGroups() As IObjArrayRef
    ' Dim result As IObjArrayRef
    Dim groups As IObjArrayRef

    Dim temp As ISqlFormatterElem

    Dim i As Long
    For i = 0 To componentArray.MaxIndex
        Set temp = componentArray.Item(i)

        Set groups = temp.getAlignmentElemGroups
        If Not groups Is Nothing Then
            If ISqlFormatterElem_getAlignmentElemGroups Is Nothing Then
                Set ISqlFormatterElem_getAlignmentElemGroups = UtilsArrayRef.createRealObjArrayRef
            End If

            ISqlFormatterElem_getAlignmentElemGroups.mergeArrRef groups
        End If
    Next

    ' return result --> ISqlFormatterElem_getAlignmentElemGroups
End Function

' ==================================================
' ISqlFormatterElem -- 前缀空格设置
' ==================================================
' 设置当前元素的嵌套深度，并计算前缀空格的数量
' 会覆盖 setPrefixSpaceCount 的设置结果
Public Sub ISqlFormatterElem_setElemNestCount(ByVal nestCount As Integer, ByRef config As SqlFormatterConfiguration)
    prefixSpaceCount = nestCount * config.elemNestLevelPrefixSpaceCount

    ' 如果存在边界括号，则每一个子列都需要额外附加一级深度
    Dim subColNestCount As Integer
    subColNestCount = nestCount
    If ISqlFormatterElem_hasBoundaryParenthesis Then
        subColNestCount = subColNestCount + 1
    End If

    ' 在额外增加一个单位的深度，表示因为 case 换行而产生的前缀空格
    subColNestCount = subColNestCount + 1

    Dim temp As ISqlFormatterElem
    Dim i As Long
    For i = 0 To componentArray.MaxIndex
        Set temp = componentArray.Item(i)
        temp.setElemNestCount subColNestCount, config
    Next
End Sub

' 直接设置前缀空格的数量
' 将会覆盖 setElemNestCount 对前缀空格数量的控制
Public Sub ISqlFormatterElem_setPrefixSpaceCount(ByVal spaceCount As Integer, ByRef config As SqlFormatterConfiguration)
    prefixSpaceCount = spaceCount

    ' 如果存在边界括号，则每一个子列都需要额外附加一个单位的前缀空格
    Dim subColPrefixSpaceCount As Integer
    subColPrefixSpaceCount = spaceCount
    If ISqlFormatterElem_hasBoundaryParenthesis Then
        subColPrefixSpaceCount = subColPrefixSpaceCount + config.elemNestLevelPrefixSpaceCount
    End If

    ' 在额外增加一个单位的深度，表示因为 case 换行而产生的前缀空格
    subColPrefixSpaceCount = subColPrefixSpaceCount + config.elemNestLevelPrefixSpaceCount

    Dim temp As ISqlFormatterElem
    Dim i As Long
    For i = 0 To componentArray.MaxIndex
        Set temp = componentArray.Item(i)
        temp.setPrefixSpaceCount subColPrefixSpaceCount, config
    Next
End Sub

' ==================================================
' ISqlFormatterElem -- 边界括号
' ==================================================
' 是否存在边界括号，有左边或者右边都返回 true
Public Function ISqlFormatterElem_hasBoundaryParenthesis() As Boolean
    If leftParenthesisCount > 0 Then
        ISqlFormatterElem_hasBoundaryParenthesis = True
    ElseIf rightParenthesisCount > 0 Then
        ISqlFormatterElem_hasBoundaryParenthesis = True
    End If

    ' return False
End Function

' 边界处的 [(] 的数量 + count 个
Public Sub ISqlFormatterElem_addBoundaryLeftParenthesis(Optional ByVal count As Integer = 1)
    leftParenthesisCount = leftParenthesisCount + count
End Sub

' 边界处的 [)] 的数量 + count 个
Public Sub ISqlFormatterElem_addBoundaryRightParenthesis(Optional ByVal count As Integer = 1)
    rightParenthesisCount = rightParenthesisCount + count
End Sub

' ==================================================
' ISqlFormatterElem -- 内容检查
' ==================================================
' 获取类型
Public Function ISqlFormatterElem_getType() As SqlTokenType
    ISqlFormatterElem_getType = SqlTokenType.KEY_CASE
End Function

' 检查当前元素是不是一个注释
Public Function ISqlFormatterElem_isComment() As Boolean
    ' return false
End Function

' 检查当前元素是不是和前一个元素处于同一行的注释
Public Function ISqlFormatterElem_isShareLineComment() As Boolean
    ' return false
End Function

' 检查当前元素是不是处于单独一行的注释
Public Function ISqlFormatterElem_isSingleLineComment() As Boolean
    ' return false
End Function

' 检查当前元素是不是以注释结尾
Public Function ISqlFormatterElem_isEndsWithComment() As Boolean
    ' 1. 如果存在右括号，则默认不是以注释结尾
    If rightParenthesisCount > 0 Then
        ' return false;
        Exit Function
    End If

    ' 2. 检查: 如果组件数组为空，默认返回 false
    If componentArray.Count = 0 Then
        ' return false;
        Exit Function
    End If

    ' 3. 获取最后一个元素，并返回其是否是注释
    Dim lastElem As ISqlFormatterElem
    Set lastElem = componentArray.getLast
    ISqlFormatterElem_isEndsWithComment = lastElem.isEndsWithComment
End Function

' 检查当前生成器是不是多行的
Public Function ISqlFormatterElem_isMultiLine() As Boolean
    ' 1. 获取组成元素的数量
    Dim componentArrayCount As Integer
    componentArrayCount = componentArray.Count

    ' 2. 检查: 如果组成元素的数量 = 0，则返回 false
    If componentArrayCount = 0 Then
        ' return false
        Exit Function

    ' 3. 否则，检查: 如果组成元素的数量 = 1
    ElseIf componentArrayCount = 1 Then
        ' ======
        ' 对于 case 关键字 + 一个同行注释的情况
        ' case --comment
        ' 如果没有边界括号，则是单行的
        ' 如果有边界括号，则是多行的
        ' (
        '     case --comment
        ' )
        ' ======

        ' 3.1 获取第一个元素到 component
        Dim component As ISqlFormatterElem
        Set component = componentArray.Item(0)

        ' 3.2 检查: 如果 component 不是和前一个元素同一行的注释，则返回 true
        If component.isShareLineComment() = False Then
            ISqlFormatterElem_isMultiLine = True
            Exit Function
        End If

        ' 3.3 否则，检查: 如果不存在边界括号，则返回 false
        ' 3.4 否则，返回 true
        ISqlFormatterElem_isMultiLine = ISqlFormatterElem_hasBoundaryParenthesis

    ' 4. 否则 (存在多个组成元素)，则返回 true
    Else
        ISqlFormatterElem_isMultiLine = True
    End If
End Function

' ==================================================
' ISqlFormatterElem -- 生成列的字符串
' ==================================================
' 生成一整行的字符串。如果本身是多行，将会使用换行符进行连接
Public Function ISqlFormatterElem_toString(ByRef config As SqlFormatterConfiguration) As String
    If ISqlFormatterElem_isMultiLine = False Then
        ' 1. 如果是单行的，则直接生成单行字符串
        ISqlFormatterElem_toString = doCreateSingleLineString(config)
        Exit Function
    Else
        ' 1. 生成原始字符集合
        Dim arrRef As IPrimitiveArrayRef
        Set arrRef = doCreateMultiLineArray(config)
        ' 2. 使用换行符连接
        ISqlFormatterElem_toString = UtilsArrayRef.reducePrimitiveArrayRefToStr(arrRef, config.lineBreak)
        Exit Function
    End If
End Function

' 生成多行字符串
' 如果只有一行，则返回的数组引用中将会只有一个
' 并且不会在开头附加前缀空格
'
' @return IPrimitiveArrayRef<String>
Public Function ISqlFormatterElem_toStringArray(ByRef config As SqlFormatterConfiguration) As IPrimitiveArrayRef
    ' ==================================================
    ' 1. 处理单行的情况
    ' ==================================================
    ' 1. 检查: 如果是单行的，则生成单行字符串
    If ISqlFormatterElem_isMultiLine = False Then
        Dim singleStr As String
        singleStr = doCreateSingleLineString(config)

        ' 2. 将 `singleStr` 包装成一个长度为 1 的结果集合，并返回
        Set ISqlFormatterElem_toStringArray = UtilsArrayRef.wrapperToPrimitiveArrayRef(singleStr)
        Exit Function
    End If

    ' ==================================================
    ' 2. 处理多行的情况
    ' ==================================================
    ' 1. 生成多行字符串集合，返回生成结果
    Set ISqlFormatterElem_toStringArray = doCreateMultiLineArray(config)
End Function

' ======================================================================
' Private Function
' ======================================================================
' 生成case组成列的单行字符串
Private Function doCreateSingleLineString(ByRef config As SqlFormatterConfiguration) As String
    ' ==================================================
    ' 1. 声明变量
    ' ==================================================
    ' 1. 结果字符串 `String result = ""`
    ' 这里直接使用 doCreateSingleLineString

    ' ==================================================
    ' 2. 处理左边界括号
    ' ==================================================
    ' 1. 如果存在左边界括号，则继续: `leftParenthesisCount > 0`
    If leftParenthesisCount > 0 Then
        ' 2. 生成等量的左括号，并连接到 `result` 的末尾
        doCreateSingleLineString = doCreateSingleLineString & UtilsString.repeatLeftParenthesis(leftParenthesisCount)
    End If

    ' ==================================================
    ' 3. 保存 case token
    ' ==================================================
    ' 1. 将 `caseToken.value` 添加到 `result`
    doCreateSingleLineString = doCreateSingleLineString & caseToken.value

    ' ==================================================
    ' 4. 处理可能存在的注释
    ' ==================================================
    ' 1. 检查: 如果组成元素数组的数量 > 0，则继续
    If componentArray.Count > 0 Then
        ' 2. 获取组成数组的第一个元素到 `ISqlFormatterElem component`
        Dim component As ISqlFormatterElem
        Set component = componentArray.Item(0)
        ' 3. 连接 `result + " " + component 的字符串`
        doCreateSingleLineString = doCreateSingleLineString & " " & component.toString(config)
    End If

    ' ==================================================
    ' 5. 处理右边界括号
    ' ==================================================
    ' 1. 如果存在右边界括号，则继续: `rightParenthesisCount > 0`
    If rightParenthesisCount > 0 Then
        ' 2. 生成等量的右括号，并连接到 `result` 的末尾
        doCreateSingleLineString = doCreateSingleLineString & UtilsString.repeatRightParenthesis(rightParenthesisCount)
    End If

    ' ==================================================
    ' 5. 返回结果集合 `result`
    ' ==================================================
    ' return result
End Function

' 获取原始的字符串集合
' 不包含前缀空格
' 不包含换行符，所以在产生多行的字符串时，返回一个数组集合
' @return IPrimitiveArrayRef<String>
Private Function doCreateMultiLineArray(ByRef config As SqlFormatterConfiguration) As IPrimitiveArrayRef
    ' ==================================================
    ' 1. 声明变量
    ' ==================================================
    ' 1. 结果集合 `IPrimitiveArrayRef<String> result = UtilsArrayRef.createRealPrimitiveArrayRef<String>(8)`
    Dim result As IPrimitiveArrayRef
    Set result = UtilsArrayRef.createRealPrimitiveArrayRef(8)

    ' 2. 前缀字符串 `String prefixSpaceStr = ""`
    Dim prefixSpaceStr As String

    ' 3. 需要连接的元素的数组引用切片: `IObjArrayRef componentsSlice`
    Dim componentsSlice As IObjArrayRef

    ' ==================================================
    ' 2. 处理前缀空格
    ' ==================================================
    ' 1. 检查: 如果当前列对象存在边界括号，则继续: `hasBoundaryParenthesis() == true`
    If ISqlFormatterElem_hasBoundaryParenthesis Then
        ' 2. 生成一个单位的前缀空格 `prefixSpaceStr += config.elemNestLevelPrefixSpace`
        prefixSpaceStr = config.elemNestLevelPrefixSpace
    End If

    ' ==================================================
    ' 3. 处理左边界括号
    ' ==================================================
    ' 1. 如果存在左边界括号，则继续: `leftParenthesisCount > 0`
    If leftParenthesisCount > 0 Then
        ' 2. 生成等量的左括号，并保存到 `result`
        result.Add UtilsString.repeatLeftParenthesis(leftParenthesisCount)
    End If

    ' ==================================================
    ' 4. 保存 case token
    ' ==================================================
    ' 1. 将 `prefixSpaceStr + caseToken.value` 添加到 `result`
    result.Add prefixSpaceStr & caseToken.value

    ' ==================================================
    ' 5. 处理 `end` 元素之前的元素
    ' ==================================================
    ' 1. 检查，如果不存在 end 元素
    If endIndex = -1 Then
        ' 1.1. 生成所有元素的字符串集合，并保存到 `result`
        SqlFormatterHelper.fullElemExclusiveRowStrArr _
            result, componentArray, config, prefixSpaceStr & config.elemNestLevelPrefixSpace


    ' 2. 否则，检查，如果 end 元素不是第一个: `endIndex != 0`
    ElseIf endIndex <> 0 Then
        ' 1. 截取需要连接的元素集合 `componentsSlice = componentArray[0, endIndex)`
        Set componentsSlice = componentArray.slice(0, endIndex)
        ' 2. 生成 `componentsSlice` 的字符串集合，并保存到 `result`
        SqlFormatterHelper.fullElemExclusiveRowStrArr _
            result, componentsSlice, config, prefixSpaceStr & config.elemNestLevelPrefixSpace
    End If

    ' ==================================================
    ' 6. 处理 end 及 end 之后的元素
    ' ==================================================
    ' 6.1. 检查: 如果存在 `end` 元素: `endIndex != -1`，则继续
    If endIndex <> -1 Then
        ' ==================================================
        ' 6.1 处理 `end` 元素
        ' ==================================================
        ' 1. 获取 `endIndex` 位置的元素: `SqlToken token`
        Dim token As SqlToken
        Set token = componentArray.Item(endIndex)
        ' 2. 将 `prefixSpaceStr + token.value` 并保存到 `result`
        result.Add prefixSpaceStr & token.value

        ' ==================================================
        ' 6.2 处理 end 后面的元素
        ' ==================================================
        ' 1. 检查: 如果 end 后面还有元素，则继续: `endIndex < componentArray.MaxIndex`
        If endIndex < componentArray.MaxIndex Then
            ' 2. 截取需要连接的元素集合 `componentsSlice = componentArray[endIndex + 1, componentArray.Count)`
            Set componentsSlice = componentArray.slice(endIndex + 1, componentArray.Count)

            ' 3. 生成 `componentsSlice` 的字符串集合，并保存到 `result`
            SqlFormatterHelper.fullElemExclusiveRowStrArr _
                result, componentsSlice, config, prefixSpaceStr
        End If
    End If

    ' ==================================================
    ' 7. 处理右边界括号
    ' ==================================================
    ' 1. 如果存在右边界括号，则继续: `rightParenthesisCount > 0`
    If rightParenthesisCount > 0 Then
        ' 2. 生成等量的右括号，并保存到 `result`
        result.Add UtilsString.repeatRightParenthesis(rightParenthesisCount)
    End If

    ' ==================================================
    ' 8. 返回结果集合 `result`
    ' ==================================================
    Set doCreateMultiLineArray = result
End Function